
/**
\file Definition of nmn::math::vector.
\author Sean Tromans
*/

#ifndef	NMN_TEMPLATES_MATH_VECTOR_HPP
#define	NMN_TEMPLATES_MATH_VECTOR_HPP

#include <boost/array.hpp>
#include <boost/type_traits/is_arithmetic.hpp>
#include <boost/operators.hpp>
#include <boost/preprocessor.hpp>

#ifndef	NMN_MATH_VECTOR_MAX_ARITY
#define	NMN_MATH_VECTOR_MAX_ARITY 4
#endif

#define NMN_MATH_VECTOR_CONSTRUCTOR( z, n, _ )	\
	template < BOOST_PP_ENUM_PARAMS_Z(z, BOOST_PP_INC(n), typename T) >	\
	vector( BOOST_PP_ENUM_BINARY_PARAMS_Z(z, BOOST_PP_INC(n), T, const & p) );

namespace nmn
{

	namespace math
	{

		/**
		Generic statically sized vector type.
		\author Sean Tromans
		*/
		template < typename TValueType, size_t Size >
		class vector	:	public	boost::additive1< typename vector< TValueType, Size >,
									boost::multipliable1< typename vector< TValueType, Size >,
									boost::multiplicative2< typename vector< TValueType, Size >, TValueType,
									boost::equality_comparable1< typename vector< TValueType, Size > > > > >
		{
			//
			//	Public Member Types
			//
			public:

				typedef	vector< TValueType, Size >			my_type;
				typedef boost::array< TValueType, Size >	array_type;
				typedef std::size_t							size_type;
				enum	{	static_size = Size	};

				typedef	TValueType			value_type;
				typedef	value_type &		reference_type;
				typedef	value_type const &	const_reference_type;

				typedef typename array_type::iterator				iterator;
				typedef typename array_type::const_iterator			const_iterator;
				typedef typename array_type::reverse_iterator		reverse_iterator;
				typedef typename array_type::const_reverse_iterator	const_reverse_iterator;

			//
			//	Public Member Functions
			//
			public:

				/**
				Default Constructor. Does nothing.
				\author Sean Tromans
				*/
				vector( );

				/**
				Initialise all components of the vector to the same value.
				@param value The value to initialise each component to.
				\author Sean Tromans
				*/
				template < typename TOtherValueType >
				vector( TOtherValueType const & value );

				/**
				Copy construct from another vector of the same size, but potentially a different type.
				@param rhs The vector to copy.
				\author Sean Tromans
				*/
				template < typename TOtherValueType >
				vector( vector< TOtherValueType, Size > const & rhs );

				/**
				Initialise a vector from array of values.
				@param values The array containing the values to initialise from.
				\author Sean Tromans
				*/
				template < typename TOtherValueType >
				vector( boost::array< TOtherValueType, Size > const & values );

				BOOST_PP_REPEAT_FROM_TO( 1, NMN_MATH_VECTOR_MAX_ARITY, NMN_MATH_VECTOR_CONSTRUCTOR, 0 );

				/**
				Assign this vector to be equal to another.
				@param rhs The vector to copy.
				\author Sean Tromans
				*/
				template < typename TOtherValueType >
				my_type & operator = ( vector< TOtherValueType, Size > const & rhs );

				/**
				The size of the vector.
				\author Sean Tromans
				*/
				size_type size( ) const;

				/**
				Component wise addition of another vector onto this.
				@param rhs The vector to be added.
				@return A reference to this after the addition.
				\author Sean Tromans
				*/
				my_type & operator += ( my_type const & rhs );

				/**
				Component wise subtraction of another vector onto this.
				@param rhs The vector to be subtracted.
				@return A reference to this after the subtraction.
				\author Sean Tromans
				*/
				my_type & operator -= ( my_type const & rhs );

				/**
				Component wise multiplication of another vector onto this.
				@param rhs The vector to be multiplied.
				@return A reference to this after the multiplication.
				\author Sean Tromans
				*/
				my_type & operator *= ( my_type const & rhs );

				/**
				Component wise multiplication of a value type onto this.
				@param rhs The value type to be multiplied.
				@return A reference to this after the multiplication.
				\author Sean Tromans
				*/
				my_type & operator *= ( value_type const & rhs );

				/**
				Component wise division of a value type onto this.
				@param rhs The value type to be divided.
				@return A reference to this after the division.
				\author Sean Tromans
				*/
				my_type & operator /= ( value_type const & rhs );

				/**
				Component wise strict equality check against another vector.
				@param rhs The vector to compare against.
				@return True if each corresponding component in the two vectors are exactly equal.
				\author Sean Tromans
				*/
				bool strict_equals( my_type const & rhs ) const;

				/**
				Gets a component from inside the vector.
				@param index The index of the component to return.
				@return a reference to the component at \c index.
				\author Sean Tromans
				*/
				reference_type operator[] ( size_type index );

				/**
				Gets a component from inside the vector.
				@param index The index of the component to return.
				@return a constant reference to the component at \c index.
				\author Sean Tromans
				*/
				const_reference_type operator[] ( size_type index ) const;

				/**
				Gets a component from inside the vector.
				@param index The index of the component to return.
				@return a reference to the component at \c index.
				\author Sean Tromans
				*/
				reference_type at( size_type index );

				/**
				Gets a component from inside the vector.
				@param index The index of the component to return.
				@return a constant reference to the component at \c index.
				\author Sean Tromans
				*/
				const_reference_type at( size_type index ) const;

				/**
				Returns an iterator to the first element in the vector.
				\author Sean Tromans
				*/
				iterator begin( );

				/**
				Returns a delimiting iterator to the vector.
				\author Sean Tromans
				*/
				iterator end( );

				/**
				Returns a constant iterator to the first element in the vector.
				\author Sean Tromans
				*/
				const_iterator begin( ) const;

				/**
				Returns a delimiting constant iterator to the vector.
				\author Sean Tromans
				*/
				const_iterator end( ) const;

				/**
				Returns a constant iterator to the first element in the vector.
				\author Sean Tromans
				*/
				const_iterator cbegin( ) const;

				/**
				Returns a delimiting constant iterator to the vector.
				\author Sean Tromans
				*/
				const_iterator cend( ) const;

				/**
				Returns a reverse iterator to the last element in the vector.
				\author Sean Tromans
				*/
				reverse_iterator rbegin( );

				/**
				Returns a delimiting reverse iterator to the vector.
				\author Sean Tromans
				*/
				reverse_iterator rend( );

				/**
				Returns a constant reverse iterator to the last element in the vector.
				\author Sean Tromans
				*/
				const_reverse_iterator rbegin( ) const;

				/**
				Returns a delimiting constant reverse iterator to the vector.
				\author Sean Tromans
				*/
				const_reverse_iterator rend( ) const;

				/**
				Swaps this vector with another.
				@param rhs The vector to swap with.
				\author Sean Tromans
				*/
				void swap( my_type & rhs );

				/**
				Retreives the raw data contained within the vector.
				\author Sean Tromans
				*/
				value_type const * data( ) const;

			//
			//	Private Member Types
			//
			private:

				array_type m_array;
				
				static_assert( static_size >= 1, "vector must have at least two components." );
				static_assert( boost::is_arithmetic< TValueType >::value, "TValueType must be a numeric value type." );
		};

		/**
		\relates nmn::math::vector
		Outputs a vector2 to an output stream.
		@param os The output ostream.
		@param vec The vector to output.
		@return A reference to the output stream.
		\author Sean Tromans
		*/
		template < typename Elem, typename Traits, typename TValueType, size_t Size >
		inline std::basic_ostream< Elem, Traits > & operator << ( std::basic_ostream< Elem, Traits > & os, vector< TValueType, Size > const & vec );

		/**
		\relates nmn::math::vector
		Component wise absolute equality check.
		@param lhs The first vector to compare.
		@param rhs The second vector to compare.
		@return true if the two vectors are exactly equal.
		\author Sean Tromans
		*/
		template < typename TValueType, size_t Size >
		bool operator == ( vector< TValueType, Size > const & lhs, vector< TValueType, Size > const & rhs );

	};

};

namespace std
{
	/**
	Swaps the values of two vectors.
	@param lhs The first vector.
	@param rhs The second vector.
	\author Sean Tromans
	*/
	template < typename TValueType, size_t Size >
	void swap( nmn::math::vector< TValueType, Size > & lhs, nmn::math::vector< TValueType, Size > & rhs );
}

#undef	NMN_MATH_VECTOR_CONSTRUCTOR
//#undef	NMN_MATH_VECTOR_SET_ARRAY

#endif	//NMN_TEMPLATES_MATH_VECTOR_HPP
